#include "ImageOperationPanel.h"
#include "../../controller/SignalsBus.h"
#include "../../models/ProjectData.h"

#include <QPushButton>
#include <QLabel>
#include <QLineEdit>
#include <QGridLayout>
#include <QRadioButton>
#include <QFileDialog>
#include <QSlider>
#include <QtConcurrent/QtConcurrent>

QHash<QString,QLineEdit*> values;
ImageOperationPanel::ImageOperationPanel():imageCount(0) {
    initComponents();

    connect(SignalsBus::getInstance(),&SignalsBus::loadImageFolder,this,[=](const QStringList& images){
        // auto future = QtConcurrent::run([=](){this->onLoadFolder(images);});
        auto future = QtConcurrent::run([this,images](){this->onLoadFolder(images);});
        auto watcher = new QFutureWatcher<void>(this);
        connect(watcher,&QFutureWatcher<void>::finished,
                watcher,&QFutureWatcher<void>::deleteLater);
        watcher->setFuture(future);
    });
}

void ImageOperationPanel::initComponents()
{
    const auto btnLoadImage = new QPushButton(tr("加载单张图像"));
    const auto btnLoadDictionary = new QPushButton(tr("加载批量图像"));
    const auto lbAlgorithmType = new QLabel(tr("检测方式"));
    const auto rbtnSingle = new QRadioButton(tr("单图"));
    const auto rbtnMultiple = new QRadioButton(tr("批量"));
    const auto btnCalculate = new QPushButton(tr("算法检测"));
    const auto lbImageDictionary = new QLabel(tr("图像目录"));
    const auto lineImageDictionary = new QLineEdit();
    const auto btnPrev = new QPushButton(tr("前一张"));
    const auto btnNext = new QPushButton(tr("下一张"));

    const auto algorithmLayout = new QHBoxLayout();
    algorithmLayout->addWidget(lbAlgorithmType,1);
    algorithmLayout->addWidget(rbtnSingle,1);
    algorithmLayout->addWidget(rbtnMultiple,1);
    algorithmLayout->addWidget(btnCalculate,1);


    const auto infoLayout = new QHBoxLayout();
    infoLayout->addWidget(lbImageDictionary,1);
    infoLayout->addWidget(lineImageDictionary,3);
    infoLayout->addWidget(btnLoadImage,1);
    infoLayout->addWidget(btnLoadDictionary,1);

    const auto buttonLayout = new QHBoxLayout();
    buttonLayout->addWidget(btnPrev);
    buttonLayout->addWidget(btnNext);

    const auto paramterWidget = initAlgorithmParameterComponents();


    const auto layout = new QGridLayout();
    layout->addLayout(algorithmLayout,0,0,1,1);
    layout->addLayout(infoLayout,1,0,1,1);
    layout->addLayout(buttonLayout,2,0,1,1);
    layout->addWidget(paramterWidget,3,0,1,1);
    values.insert("imageFile",lineImageDictionary);

    setLayout(layout);
    setTitle(tr("图像算法检测面板"));

    connect(btnLoadImage,&QPushButton::clicked,this,&ImageOperationPanel::onLoadImage);
    connect(btnLoadDictionary,&QPushButton::clicked,this,&ImageOperationPanel::onLoadDictionary);
    connect(btnCalculate,&QPushButton::clicked,this,[=](){
        if(rbtnSingle->isChecked())
            onSingleCheck();
        if(rbtnMultiple->isChecked())
        {
            auto future = QtConcurrent::run([=](){this->onMultipleCheck();});
            auto watcher = new QFutureWatcher<void>(this);
            connect(watcher,&QFutureWatcher<void>::finished,watcher,&QFutureWatcher<void>::deleteLater);
            watcher->setFuture(future);

            // onMultipleCheck();
        }
    });

}

QWidget* ImageOperationPanel::initAlgorithmParameterComponents()
{
    const auto lbMinArea = new QLabel(tr("最小面积"));
    const auto lbMinNumber = new QLabel(tr("黑点阴影最小数量"));
    const auto lbMinRatio = new QLabel(tr("轮廓尺寸与图片比例"));
    const auto lbMaskSize = new QLabel(tr("屏蔽区域"));
    const auto lbMinDiameter = new QLabel(tr("黑点阴影最小直径(mm)"));
    const auto lbPixelSize = new QLabel(tr("像元尺寸(mm/pixel)"));

    const auto lineMinArea = new QLineEdit();
    const auto lineMinNumber = new QLineEdit();
    const auto lineMinRatio = new QLineEdit();
    const auto lineMaskSize = new QLineEdit();
    const auto lineMinDiameter = new QLineEdit();
    const auto linePixelSize = new QLineEdit();

    const auto sliderMinRatio = new QSlider(Qt::Horizontal);
    sliderMinRatio->setRange(0,100);
    const auto sliderMaskSize = new QSlider(Qt::Horizontal);
    sliderMaskSize->setRange(0,100);

    values.insert("minArea",lineMinArea);
    values.insert("minNumber",lineMinNumber);
    values.insert("minRatio",lineMinRatio);
    values.insert("maskSize",lineMaskSize);
    values.insert("minDiameter",lineMinDiameter);
    values.insert("pixelSize",linePixelSize);

    const auto parameterLayout = new QGridLayout();

    parameterLayout->addWidget(lbMinArea,0,0,1,1);
    parameterLayout->addWidget(lineMinArea,0,1,1,1);
    parameterLayout->addWidget(lbMinNumber,0,2,1,1);
    parameterLayout->addWidget(lineMinNumber,0,3,1,1);
    parameterLayout->addWidget(lbMinDiameter,1,0,1,1);
    parameterLayout->addWidget(lineMinDiameter,1,1,1,1);
    parameterLayout->addWidget(lbPixelSize,1,2,1,1);
    parameterLayout->addWidget(linePixelSize,1,3,1,1);
    parameterLayout->addWidget(lbMinRatio,2,0,1,1);
    parameterLayout->addWidget(sliderMinRatio,2,1,1,2);
    parameterLayout->addWidget(lineMinRatio,2,3,1,1);
    parameterLayout->addWidget(lbMaskSize,3,0,1,1);
    parameterLayout->addWidget(sliderMaskSize,3,1,1,2);
    parameterLayout->addWidget(lineMaskSize,3,3,1,1);

    lineMinArea->setText("30");
    lineMinNumber->setText("0");
    lineMinRatio->setText("0.3");
    lineMaskSize->setText("21");
    lineMinDiameter->setText("3");
    linePixelSize->setText("1");


    connect(sliderMinRatio,&QSlider::valueChanged,this,[=](int value){
        double showValue = (double)value/100;
        lineMinRatio->setText(QString::number(showValue));
    });

    connect(sliderMaskSize,&QSlider::valueChanged,this,[=](int value){
        lineMaskSize->setText(QString::number(value));
    });

    const auto paramterWidget = new QWidget();
    paramterWidget->setLayout(parameterLayout);
    return paramterWidget;

}

void ImageOperationPanel::onLoadDictionary()
{

}

void ImageOperationPanel::onLoadImage()
{
    const auto image = QFileDialog::getOpenFileName(this,tr("选取图片"),"/",tr("*.bmp;*.jpg;*.tif;*.png"));
    values["imageFile"]->setText(image);
    emit SignalsBus::getInstance()->onLoadSingleImage(image);


    HalconCpp::ReadImage(&AlgorithmParameters::getInstance()->srcImage,image.toStdString().c_str());
}

void ImageOperationPanel::onSingleCheck()
{
    AlgorithmParameters::getInstance()->minArea = values["minArea"]->text().toDouble();
    AlgorithmParameters::getInstance()->minNumber = values["minNumber"]->text().toDouble();
    AlgorithmParameters::getInstance()->minDameter = values["minDiameter"]->text().toDouble();
    AlgorithmParameters::getInstance()->pixelSize = values["pixelSize"]->text().toDouble();
    AlgorithmParameters::getInstance()->minRatio = values["minRatio"]->text().toDouble();
    AlgorithmParameters::getInstance()->maskSize = values["maskSize"]->text().toDouble();

    try{
        Check(AlgorithmParameters::getInstance()->srcImage,
             &AlgorithmParameters::getInstance()->resultImage,
              AlgorithmParameters::getInstance()->minArea,
              AlgorithmParameters::getInstance()->minNumber,
              AlgorithmParameters::getInstance()->minRatio,
              AlgorithmParameters::getInstance()->maskSize,
              AlgorithmParameters::getInstance()->minDameter,
              AlgorithmParameters::getInstance()->pixelSize,
             &AlgorithmParameters::getInstance()->result);

        emit SignalsBus::getInstance()->onSingleImageChecked();
    }
    catch(HalconCpp::HException exception)
    {
        qDebug()<<tr("测算失败");
    }
}

void ImageOperationPanel::onMultipleCheck()
{
    if(!ProjectData::getInstance()->algorithmHash.empty())
    {
        qDebug()<<"Multi algorithm data have been loaded";
        // emit SignalsBus::getInstance()->switchStatus(1);

        QMetaObject::invokeMethod(qApp,[](){
            emit SignalsBus::getInstance()->switchStatus(1);
        },Qt::QueuedConnection);

        int cursor = 0;
        int totalCount = ProjectData::getInstance()->algorithmHash.count();
        for(auto& data:ProjectData::getInstance()-> algorithmHash)
        {

            Check(data->srcImage,
                  &data->resultImage,
                  data->minArea,
                  data->minNumber,
                  data->minRatio,
                  data->maskSize,
                  data->minDameter,
                  data->pixelSize,
                  &data->result);

            double percent = static_cast<double>(cursor)/totalCount;

            int percentProgress = static_cast<int>(percent*100);


            QMetaObject::invokeMethod(qApp,[percentProgress,data](){
                emit SignalsBus::getInstance()->checkPercent(percentProgress);
                qDebug()<<"Image "<<ProjectData::getInstance()->algorithmHash.key(data)<<" has been checked,result is "<<data->result.ToString();

            },Qt::QueuedConnection);

            cursor++;
        }
        QMetaObject::invokeMethod(qApp,[](){
            emit SignalsBus::getInstance()->multipleCheckEnd();
            emit SignalsBus::getInstance()->switchStatus(2);
        },Qt::QueuedConnection);

    }

}

void ImageOperationPanel::onLoadFolder(const QStringList &images)
{
    try{
        if(!images.empty())
        {
            QMetaObject::invokeMethod(qApp,[](){
                emit SignalsBus::getInstance()->switchStatus(1);
            });

            imageCount = images.count();
            for(const auto& item:images)
            {
                HalconCpp::HObject image;
                HalconCpp::HTuple name(item.toStdString().c_str());
                // qDebug()<<"loade image: "<<name.ToString();
                HalconCpp::ReadImage(&image,name);
                objectList.insert(item,image);
            }

            int imageCursor = 0;
            int totalCount = objectList.count();
            for(const auto& item:std::as_const(objectList))
            {
                auto algorithm = new AlgorithmData();
                auto imageName = objectList.key(item);
                qDebug()<<"loaded image: "<<imageName;

                algorithm->minArea = values["minArea"]->text().toDouble();
                algorithm->minNumber = values["minNumber"]->text().toDouble();
                algorithm->minDameter = values["minDiameter"]->text().toDouble();
                algorithm->pixelSize = values["pixelSize"]->text().toDouble();
                algorithm->minRatio = values["minRatio"]->text().toDouble();
                algorithm->maskSize = values["maskSize"]->text().toDouble();
                algorithm->srcImage = item;

                ProjectData::getInstance()->algorithmHash.insert(imageCursor,algorithm);

                double percent = static_cast<double>(imageCursor)/totalCount;
                int percentProgress = static_cast<int>(percent*100);

                QMetaObject::invokeMethod(qApp,[percentProgress](){
                    emit SignalsBus::getInstance()->checkPercent(percentProgress);
                },Qt::QueuedConnection);


                imageCursor++;

                qDebug()<<"Insert image "<<ProjectData::getInstance()->algorithmHash.key(algorithm)<<" into algorithm queue";
            }
            QMetaObject::invokeMethod(qApp,[](){
                emit SignalsBus::getInstance()->switchStatus(2);
                emit SignalsBus::getInstance()->checkPercent(0);
            });


        }
    }catch(HalconCpp::HException exception)
    {
        QString msg;
        msg = QString("Error Code: %1 \n Error Message: %2").arg(exception.ErrorCode()).arg(exception.ErrorMessage().Text());
        qDebug()<<msg;
    }
}
